package com.example.dynamicschedule.task;


import com.alibaba.fastjson.JSON;
import com.example.dynamicschedule.config.ScanningCleaningConfig;
import com.example.dynamicschedule.utils.FileDownloadUtil;
import com.github.pagehelper.util.StringUtil;
import com.jcraft.jsch.*;
import org.apache.commons.collections4.CollectionUtils;
;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Repository
public class UploadFileTask {
    @Autowired
    private ScanningCleaningConfig scanningCleaningConfig;
    @Autowired
    private FileDownloadUtil fileDownloadUtil;

    //ftp服务器地址
    @Value("${ftpHostname}")
    public String hostname;
    //ftp服务器端口号默认为21
    @Value("${ftpPort}")
    public Integer port;
    //ftp登录账号
    @Value("${ftpUsername}")
    public String username;
    //ftp登录密码
    @Value("${ftpPassword}")
    public String password;
    /**
     * jcraft的ChannelSftp类实例，信息传输通道
     */
    private ChannelSftp channelSftp=null;
    /**
     * 当前操作路径
     */
    private String currentPath;
    private Session session = null;

    static Logger log = Logger.getLogger(UploadFileTask.class);

    /**
     * 取得当前的ChannelSftp实例
     *
     * @return 当前的ChannelSftp实例
     */
    public ChannelSftp getChannelSftp() {
        return channelSftp;
    }
    @Transactional
    public void UploadFileTask() throws Exception {
        Date date = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
        String dateStr = simpleDateFormat.format(date);
        log.info("上传定时任务开始");
        //上传短信
        uploadMessage(dateStr);
        //上传智能外呼
        uploadAICall(dateStr);
        //上传人工外呼
        uploadPeopleCall(dateStr);
        log.info("上传定时任务结束");
    }

    /**
     * 获取所上传文件名
     *
     * @param txtPath 当地文件地址
     * @return
     */
    private List<String> getFileNameList(String txtPath) {
        Date date = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
        String dateStr = simpleDateFormat.format(date);
        File file = new File(txtPath);
        String[] list = file.list();
        log.info("获取文件名称开始");
        List<String> fileList = new ArrayList<>();
        if (list != null) {
            if (list.length > 0) {
                for (String s : list) {
                    if (s.contains(dateStr)) {
                        fileList.add(s);
                    }
                }
            }
        }

        log.info("获取文件名称结束,文件名称为:" + JSON.toJSONString(fileList));
        return fileList;
    }

    /**
     * 人工外呼上传
     *
     * @param dateStr
     */
    private void uploadPeopleCall(String dateStr) {
        log.info("上传人工外呼开始");
        String peopleUploadJsonPath = scanningCleaningConfig.getPeopleUploadJsonPath();
        List<String> peopleUploadFileNameList = getFileNameList(peopleUploadJsonPath);
        if (CollectionUtils.isNotEmpty(peopleUploadFileNameList)) {
            String jsonFileName = null;
            String okFileName = null;
            for (String fileName : peopleUploadFileNameList) {
                if (fileName.contains("json")) {
                    jsonFileName = fileName;
                } else if (fileName.contains("ok")) {
                    okFileName = fileName;
                }
            }
            log.info("人工上传到oss上json文件开始");
            String localFileNameJosnStr = peopleUploadJsonPath + jsonFileName;
            String uploadJsonName = new StringBuilder(scanningCleaningConfig.getPackageName()).append("people/").append(scanningCleaningConfig.getPrefixUploadJsonPath().replace("yyyyMMdd", dateStr)).toString();
            File localFileNameJson = new File(localFileNameJosnStr);
            fileDownloadUtil.uploadOSS(uploadJsonName, localFileNameJson);
            log.info("人工上传到oss上json文件结束,上传文件地址为:" + uploadJsonName);
            log.info("人工上传到oss上ok文件开始");
            String localFileNameOkStr = peopleUploadJsonPath + okFileName;
            String uploadOkName = new StringBuilder(scanningCleaningConfig.getPackageName()).append("people/").append(scanningCleaningConfig.getPrefixUploadJsonPath().replace("yyyyMMdd", dateStr).replace("json", "ok")).toString();
            File localFileNameOk = new File(localFileNameOkStr);
            fileDownloadUtil.uploadOSS(uploadOkName, localFileNameOk);
            log.info("人工上传到oss上ok文件结束,上传文件地址为:" + uploadOkName);
        } else {
            log.info("人工无上传文件");
        }
        log.info("上传人工外呼结束");
    }

    /**
     * 智能外呼上传
     *
     * @param dateStr
     */
    private void uploadAICall(String dateStr) {
        log.info("上传智能外呼开始");
        //获取智能外呼文件名称
        String intelligenceJsonPath = scanningCleaningConfig.getIntelligenceUploadJsonPath();
        List<String> fileNameList = getFileNameList(intelligenceJsonPath);
        if (CollectionUtils.isNotEmpty(fileNameList)) {
            String jsonFileName = null;
            String okFileName = null;
            for (String fileName : fileNameList) {
                if (fileName.contains("json")) {
                    jsonFileName = fileName;
                } else if (fileName.contains("ok")) {
                    okFileName = fileName;
                }
            }
            log.info("智能上传到oss上json文件开始");
            String localFileNameJosnStr = intelligenceJsonPath + jsonFileName;
            String uploadJsonName = new StringBuilder(scanningCleaningConfig.getPackageName()).append("collection_online_loan_intelligence/").append(scanningCleaningConfig.getPrefixUploadJsonPath().replace("yyyyMMdd", dateStr)).toString();
            File localFileNameJson = new File(localFileNameJosnStr);
            fileDownloadUtil.uploadOSS(uploadJsonName, localFileNameJson);
            log.info("智能上传到oss上json文件结束,上传文件地址为:" + uploadJsonName);
            log.info("智能上传到oss上ok文件开始");
            String localFileNameOkStr = intelligenceJsonPath + okFileName;
            String uploadOkName = new StringBuilder(scanningCleaningConfig.getPackageName()).append("collection_online_loan_intelligence/").append(scanningCleaningConfig.getPrefixUploadJsonPath().replace("yyyyMMdd", dateStr).replace("json", "ok")).toString();

            File localFileNameOk = new File(localFileNameOkStr);
            fileDownloadUtil.uploadOSS(uploadOkName, localFileNameOk);
            log.info("智能上传到oss上ok文件结束,上传文件地址为:" + uploadOkName);
        } else {
            log.info("智能无上传文件");
        }
        log.info("上传智能外呼结束");
    }

    /**
     * 短信上传
     *
     * @param dateStr
     */
    private void uploadMessage(String dateStr) {
        log.info("上传短信文件开始");
        try {
            //获取文件所在的目录
            String txtPath = scanningCleaningConfig.getTxtUploadPath();
            //获取上传文件名
            List<String> messageFileNameList = getFileNameList(txtPath);
            List<String> txtStrList = new ArrayList<>();
            for (String s : messageFileNameList) {
                txtStrList.add(s);
            }
            if (CollectionUtils.isNotEmpty(txtStrList)) {
                this.connectServer();
                this.existDir(scanningCleaningConfig.getTxtOnlinePath());
                for (String fileName : txtStrList) {
                    String localFileName = new StringBuilder(txtPath).append(fileName).toString();
                    this.upload(scanningCleaningConfig.getTxtOnlinePath()+fileName, localFileName);
                }
            } else {
                log.info("无上传短信文件");
            }

        } catch (IOException e) {
            log.error("上传短信文件失败", e);
        }
        log.info("上传短信文件结束");
    }
    /**
     * 链接远程ftp服务器
     */
    public void connectServer() {
        String errorMsg = "";
        try {
            JSch jsch = new JSch();
            session = jsch.getSession(username, hostname, port);
            session.setPassword(password);
            // session.setUserInfo(new SftpUserInfo(ftpPassword));
            Properties sshConfig = new Properties();
            sshConfig.put("StrictHostKeyChecking", "no");
            session.setConfig(sshConfig);
            session.connect();
            channelSftp = (ChannelSftp) session.openChannel("sftp");
            channelSftp.connect();
            if(StringUtil.isNotEmpty("UTF-8")) {
                channelSftp.setFilenameEncoding("UTF-8");
            }
            currentPath=channelSftp.getHome();

        }
        catch (SftpException e) {
            errorMsg = "无法使用SFTP传输文件!";
            e.printStackTrace();
            log.error(errorMsg, e);
            throw new RuntimeException(errorMsg);

        }
        catch (JSchException e) {
            errorMsg = "没有权限与SFTP服务器连接!";
            e.printStackTrace();
            log.error(errorMsg, e);
            throw new RuntimeException(errorMsg);
        }
    }

    /**
     * 判断目录是否存在
     *
     * @param remoteDirPath ftp地址
     * @return
     */
    public boolean existDir(String remoteDirPath) throws IOException {
        boolean result = false;
        if (dirValidation(remoteDirPath)) {
            try{
                this.channelSftp.cd(remoteDirPath);
                result = true;
            }catch(SftpException sException){
                if(ChannelSftp.SSH_FX_NO_SUCH_FILE == sException.id){
                    createDirectories(remoteDirPath);
                }
            }
        }
        return result;
    }
    /**
     * 创建多级目录
     * @param pathName 相对路径
     * @return
     * @throws IOException
     */
    public boolean createDirectories(String pathName) throws IOException {

        int flag = 0;

        String[] paths = pathName.split("/");

        for(String p : paths){
            if ("".equalsIgnoreCase(p)) {
                p="/";
            }
            if(!changeDir(p)){
                if(makeDir(p)){
                    if(changeDir(p)){
                        flag += 1;
                    }
                }
            }else{
                flag += 1;
            }

        }

        if(flag == paths.length){
            return true;
        }

        return false;
    }
    /**
     * 创建目录
     *
     * @param remotePath
     *            远程目录
     * @return 操作结果，成功为true，失败为false
     */
    public boolean makeDir(String remotePath) {
        boolean result = false;
        if (dirValidation(remotePath)) {
            String testPath = getOperationPath(remotePath);
            try {
                channelSftp.mkdir(testPath);
                result = true;
            }
            catch (SftpException e) {
                log.error("创建目录'" + remotePath + "'时发生错误：", e);
                throw new RuntimeException("创建目录'" + remotePath + "'时发生错误：" + e.getMessage());
            }
        }
        return result;
    }

    /**
     * 变换当前目录
     *
     * @param path
     *            希望进入的目录
     * @return 成功为true，失败返回false
     */
    public boolean changeDir(String path) {
        boolean result = false;
        if (dirValidation(path)) {
            String testPath = getOperationPath(path);
            try {
                channelSftp.cd(testPath);
                this.currentPath = testPath;
                result = true;
            } catch (SftpException e) {
                log.error("变换目录'" + path + "'时发生错误(无该文件目录创建)");
               // throw new RuntimeException("变换目录'" + path + "'时发生错误：" + e.getMessage());
            }
        }
        return result;
    }
    /**
     * 内部方法，获得操作路径
     *
     * @param path
     *      参数
     * @return String
     */
    private String getOperationPath(String path) {
        String operactePath;
        if (!"".equals(path.trim())&&'/' == path.charAt(0)) {
            operactePath = path;
        }
        else {
            operactePath = currentPath + path;
        }
        if (operactePath.lastIndexOf("/") != operactePath.length() - 1) {
            operactePath = operactePath + "/";
        }
        return operactePath;
    }

    /**
     * 内部方法，验证路径和sftp连接
     *
     * @param path
     *            路径
     * @return 验证结果
     */
    private boolean dirValidation(String path) {
        boolean result = true;
        if (channelSftp == null || !channelSftp.isConnected()) {
            result = false;
        }
        if (null != path && "".equals(path)) {
            result = false;
            log.error("操作出现异常：指定的path不能为空");
        }
        return result;
    }


    /**
     * 上传文件
     *
     * @return -1 文件不存在 -2 文件内容为空 >0 成功上传，返回文件的大小
     * @param remoteFilePath
     *            远程文件名  remoteFilePath = 远程目录+文件名
     * @param localFilepath
     *            本地文件名
     */
    @SuppressWarnings("finally")
    public long upload(String remoteFilePath, String localFilepath) {
        if (channelSftp == null || !channelSftp.isConnected()) {
            throw new RuntimeException("上传文件" + localFilepath + "时发生异常，channelSftp连接未创建");
        }
        InputStream is = null;
        long result = -1;
        try {
            java.io.File fileIn = new java.io.File(localFilepath);

            if (fileIn.exists()) {
                is = new FileInputStream(fileIn);
                result = fileIn.length();
                // 从本地上传到服务器
                channelSftp.put(is, remoteFilePath);
            }
            else {
                result = -1;
            }
        }
        catch (IOException e) {
            result = -1;
            log.error(e.getMessage() + ": 上传文件时发生错误！",e);
            throw new RuntimeException(e.getMessage() + ": 上传文件时发生错误！");
        }
        catch (SftpException e) {
            log.error(e.getMessage() + ": 上传文件时发生错误！",e);
            throw new RuntimeException(e.getMessage() + ": 上传文件时发生错误！");
        }
        finally {
            closeInputStream(is);
        }
        return result;
    }
    /**
     * 内部方法，关闭InputStream
     *
     * @param is
     *            希望关闭的InputStream
     */
    private void closeInputStream(InputStream is) {
        try {
            if (null != is) {
                is.close();
            }
        }
        catch (IOException e) {
            log.error(e.getMessage() + ": 关闭OutputStream时出现异常！",e);
            throw new RuntimeException("关闭OutputStream时出现异常：" + e.getMessage());
        }
    }
}
